iT邦幫忙

2025 iThome 鐵人賽

DAY 5
1
Modern Web

現在就學Node.js系列 第 5

CommonJS (CJS) vs ES Modules (ESM) — Node.js 模組系統 - Day5

  • 分享至 

  • xImage
  •  

在前幾天,我們已經認識了 Node.js 的基礎與專案骨架。

今天要進入一個非常重要的主題:模組系統 (Modules)

模組是程式碼的組裝單位,幫助我們拆分功能、重複利用,避免程式碼一坨混在一起。

在 Node.js 世界裡,目前存在 兩套模組系統

  • CommonJS (CJS) → Node.js 早期的傳統方案
  • ES Modules (ESM) → JavaScript 官方標準,與瀏覽器一致

為什麼會有兩套模組系統?

  • CommonJS (CJS)

    Node.js 在 2009 年誕生時,瀏覽器還沒有標準化的模組語法。

    為了管理大型程式碼,社群制定了 CommonJS,使用 require / module.exports

    npm 上大量套件都是基於 CJS 開發的。

  • ES Modules (ESM)

    2015 年(ES6)JavaScript 終於有了官方模組系統:import / export

    它支援靜態分析、tree-shaking,並與瀏覽器保持一致。

👉CJS 是 Node.js 的傳統,ESM 是未來趨勢


如何在 Node.js 選擇模組系統?

Node.js 提供幾種方式來決定 .js 檔案的解讀方式:

  1. 預設:沒有特別設定 → .js 當成 CJS。

  2. 使用副檔名

    • .cjs → 永遠視為 CJS
    • .mjs → 永遠視為 ESM
  3. 設定 package.json

    {
      "type": "module"
    }
    

    這樣專案裡的 .js 會預設是 ESM,要寫 CJS 就得用 .cjs


CJS vs ESM 差異比較

項目 CommonJS (CJS) ES Modules (ESM)
副檔名 .cjs / .js(預設) .mjs.js(需 package.json 設定 "type": "module"
匯入 const x = require('./x') import x from './x.js'
匯出 module.exports = ...exports.a = ... export default ...export const a = ...
載入方式 同步(執行時解析) 非同步(編譯期分析,可用 top-level await
相容性 舊專案與大部分套件 新專案趨勢,與瀏覽器標準一致

📦 Export / Import 寫法

CommonJS (CJS)

// math.cjs
function add(a, b) {
  return a + b;
}
function subtract(a, b) {
  return a - b;
}

module.exports = { add, subtract };

// app.cjs
const { add, subtract } = require('./math.cjs');
console.log(add(3, 5));      // 8
console.log(subtract(10, 4)); // 6


ES Modules (ESM)

方式一:default export

// math.mjs
export default function add(a, b) {
  return a + b;
}

// app.mjs
import add from './math.mjs';
console.log(add(2, 3)); // 5

方式二:named export

// math.mjs
export function add(a, b) {
  return a + b;
}
export function subtract(a, b) {
  return a - b;
}

// app.mjs
import { add, subtract } from './math.mjs';
console.log(add(4, 6));      // 10
console.log(subtract(9, 2)); // 7

📌 注意:ESM 匯入相對路徑時,副檔名必須寫 .js.mjs,否則會報錯。


模組系統混用情境

在實際開發專案中,常會遇到「CJS 與 ESM 混用」的情況:

  • 在 ESM 專案裡引入 CJS:

    // app.mjs (ESM)
    import pkg from "lodash";   // lodash 是 CJS
    console.log(pkg.camelCase("hello world"));
    

👉 Node.js 會自動幫你把 CJS 的 module.exports 當成 default export

所以在 ESM 裡匯入 CJS,要用 import pkg from ...,而不是 {}

  • 在 CJS 專案裡引入 ESM:

    // app.cjs
    (async () => {
      const esm = await import("./esm.mjs");
      console.log(esm.hello()); 
    })();
    

👉 在 CJS 專案裡引入 ESM,只能用 import(),而且要在 非同步環境下。

模組系統整理

功能 CommonJS (CJS) ES Modules (ESM)
匯入 const x = require('./x') import x from './x.js'
匯出(單一) module.exports = foo export default foo
匯出(多個) exports.a = 1 export const a = 1
動態載入 require("./x") const x = await import("./x.js")

Node.js 新專案趨勢

Node.js 正在逐漸與 瀏覽器標準接軌

  1. 模組系統 → 新專案多採用 ESM,避免 require / module.exports
  2. 內建 APIfetchURLTextEncoder 已在 Node.js 內建,與瀏覽器一致。
  3. 工具鏈 → Vite、Next.js、Nuxt 等框架全面以 ESM 為主。

未來的 Node.js 會越來越像瀏覽器,你只要會 JavaScript,在前端怎麼寫,後端幾乎也能照樣寫。

小結

今天學到:

  • 為什麼 Node.js 同時存在 CJS 與 ESM
  • 兩者的差異、歷史背景,以及如何選擇
  • 匯入 / 匯出的寫法
  • 混用情境與注意事項
  • 模組系統整理

上一篇
Node.js 基本語法入門 -Day4
系列文
現在就學Node.js5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言